package repeater

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"sync"
	"time"
)

type Metrics struct {
	metrics map[string]*RepeaterMetrics
}

func NewMetrics(r *RepeaterController) *Metrics {
	m := &Metrics{
		metrics: make(map[string]*RepeaterMetrics),
	}
	for _, rp := range r.repeaters {
		m.metrics[rp.Name()] = &RepeaterMetrics{}
	}
	var names []string
	for k, v := range m.metrics {
		names = append(names, k)
		r.restServer.GET("/repeaters/"+k, func(c *gin.Context) {
			c.JSON(200, v)
		})
	}
	r.restServer.GET("/repeaters", func(c *gin.Context) {
		c.JSON(200, names)
	})
	return m
}

func (m *Metrics) WrappedHandle(dpkg *DataPointPack, r Repeater) error {
	mt, ok := m.metrics[r.Name()]
	if ok {
		return mt.OnHandle(dpkg, r.Handle)
	}
	return fmt.Errorf("no metric registered %s", r.Name())
}

//func (m *Metrics) WrappedHandle(dp *common.DataPointWithOffset, r Repeater) (*common.DataPointWithOffset, error) {
//    m.dps.Inc(dp)
//    if metric, ok := m.metrics[r.Name()]; ok {
//        return metric.OnHandle(dp, r.Handle)
//    }
//    return r.Handle(dp)
//}
func (m *Metrics) WrappedCommit(r Repeater) (int64, error) {
	if metric, ok := m.metrics[r.Name()]; ok {
		return metric.OnCommit(r.Commit)
	}
	return r.Commit()
}

type RepeaterMetrics struct {
	mu                      sync.RWMutex
	PackInputCount          int64 `json:"pack_input_count"`
	DataInputCount          int64 `json:"data_input_count"`
	LastPackHandleDuration  int64 `json:"last_pack_handle_duration"`
	TotalPackHandleDuration int64 `json:"total_pack_handle_duration"`
	CommitedCount           int64 `json:"commited_count"`
	UncommitedCount         int64 `json:"uncommited_count"`
	CommitedTimes           int64 `json:"commited_times"`
	LastCommitStartTime     int64 `json:"last_commit_starttime"`
	TotalCommitDuration     int64 `json:"total_commit_duration"`
	LastCommitDuration      int64 `json:"last_commit_duration"`
	LastMessageTime         int64 `json:"last_message_time"`
	LastMessageOffset       int64 `json:"last_message_offset"`
	LastCommitResult        bool  `json:"last_commit_result"`
}

func (fm *RepeaterMetrics) OnHandle(dpkg *DataPointPack, fn func(*DataPointPack) error) error {
	fm.mu.Lock()
	defer fm.mu.Unlock()
	sw := time.Now().UnixNano()
	fm.UncommitedCount += int64(dpkg.count)
	fm.DataInputCount += int64(dpkg.count)
	fm.PackInputCount += 1
	err := fn(dpkg)
	fm.LastPackHandleDuration = int64((time.Now().UnixNano() - sw) / 1000000)
	fm.TotalPackHandleDuration += fm.LastPackHandleDuration
	fm.LastMessageTime = int64(dpkg.msgTime)
	fm.LastMessageOffset = time.Now().Unix() - int64(dpkg.msgTime)
	return err
}

func (fm *RepeaterMetrics) OnCommit(fn func() (int64, error)) (int64, error) {
	fm.mu.Lock()
	defer fm.mu.Unlock()
	fm.LastCommitStartTime = time.Now().Unix()
	sw := time.Now().UnixNano()
	fm.CommitedTimes++
	o, err := fn()
	fm.LastCommitDuration = int64((time.Now().UnixNano() - sw) / 1000000)
	fm.TotalCommitDuration += fm.LastCommitDuration
	if err == nil {
		fm.CommitedCount += fm.UncommitedCount
		fm.UncommitedCount = 0
		fm.LastCommitResult = true
		return o, err
	}
	fm.LastCommitResult = false
	return o, err
}

//
//type DataPointsMetric struct {
//    mu         sync.RWMutex
//    Datapoints map[string]*DataPointMetric
//}
//
//func NewDataPointsMetric() *DataPointsMetric {
//    return &DataPointsMetric{
//        Datapoints: make(map[string]*DataPointMetric),
//    }
//}
//func (dsm *DataPointsMetric) Inc(dp *common.DataPointWithOffset) {
//    dsm.mu.Lock()
//    defer dsm.mu.Unlock()
//    if addr, err := common.ExtractMetricAddr(dp.Name); err == nil {
//        _, ok := dsm.Datapoints[addr]
//        if !ok {
//            dsm.Datapoints[addr] = &DataPointMetric{}
//        }
//        dsm.Datapoints[addr].inc(dp)
//    }
//}
//func (dsm *DataPointsMetric) MetricAddrs() (ret []string) {
//    dsm.mu.RLock()
//    defer dsm.mu.RUnlock()
//    for k := range dsm.Datapoints {
//        ret = append(ret, k)
//    }
//    return
//}
//
//func (dsm *DataPointsMetric) MetricByAddr(addr string) *DataPointMetric {
//    dsm.mu.RLock()
//    defer dsm.mu.RUnlock()
//    return dsm.Datapoints[addr]
//}
//
//type DataPointMetric struct {
//    MetricNames       []string `json:"metric_names"`
//    LastMessageOffset uint32   `json:"last_message_offset"`
//    LastMessageTime   uint32   `json:"last_message_time"`
//}
//
//func (dm *DataPointMetric) inc(dp *common.DataPointWithOffset) {
//    ok := false
//    for _, s := range dm.MetricNames {
//        if ok = (s == dp.Name);ok {
//            break
//        }
//    }
//    if !ok {
//        dm.MetricNames = append(dm.MetricNames, dp.Name)
//    }
//    dm.LastMessageTime = dp.Time
//    dm.LastMessageOffset = uint32(time.Now().Unix()) - dm.LastMessageTime
//}
